/*******************************************************************************
* This file implements the Command Line Interface.
*
* Author: Kong Wai Weng @ Cytron Technologies Sdn. Bhd.
*******************************************************************************/



#include "cml.h"



/*******************************************************************************
* PRIVATE FUNCTION PROTOTYPES                                                  *
*******************************************************************************/




/*******************************************************************************
* PRIVATE GLOBAL VARIABLES                                                     *
*******************************************************************************/

// Buffer.
static char pcUartWriteBuffer[100];
static char pcUartReadBuffer[100];



/*******************************************************************************
* TASK: taskCml
*
* DESCRIPTIONS:
* This task implements the command line interface.
*
*******************************************************************************/
portTASK_FUNCTION(taskCml, pvParameters)
{
	unsigned char i = 0;
	
	
	// Wait until we receive "enter" key.
	// Check only once per 1 seconds.
	while (1) {
		// If there is new data available.
		if (ucGetNumberOfBytesFromPc() > 0) {
			ucReadFromPc(&pcUartReadBuffer[0], 1);
			
			if (pcUartReadBuffer[0] == '\r') {
				pcUartReadBuffer[0] = 0;
				break;
			}	
		}	
		vTaskDelay(configTICK_RATE_HZ);
	}	
	
	
	// Print the title.
	sprintf(pcUartWriteBuffer, "\r\n--- EasyPilot v1.0 Command Line Interface ---\r\n\r\n\r\n>");
	while (ucGetSendToPcSpace() < strlen(pcUartWriteBuffer)) {
		// If we are calling from task, delay for 10ms to allow other task to run.
		if (xTaskGetCurrentTaskHandle() != NULL) {
			vTaskDelay(configTICK_RATE_HZ / 100);
		}	
	}	
	ucSendToPc(pcUartWriteBuffer, strlen(pcUartWriteBuffer));
	
	
	
	// We need to initialise xLastFlashTime prior to the first call to vTaskDelayUntil().
	portTickType xLastFlashTime = xTaskGetTickCount();
	
	while (1) {
		
		// If there is new data available.
		if (ucGetNumberOfBytesFromPc() > 0) {
			// Loop until we get the "enter" key or there is no data available.
			do {
				ucReadFromPc(&pcUartReadBuffer[i], 1);
				
				// Echo.
				ucSendToPc(&pcUartReadBuffer[i], 1);
				
				// Break if we receive the \r.
				if (pcUartReadBuffer[i] == '\r') {
					break;
				}
				
				// Delete a character if we received backspace.
				if (pcUartReadBuffer[i] == 0x08) {
					if (i >= 2) {
						// We need to -2 because need to include the backspace itself.
						i -= 2;
					}	
				}
				
				// Increase the pointer.
				if (++i >= sizeof(pcUartReadBuffer)) {
					i = 0;
				}	
				
				
			} while (ucGetNumberOfBytesFromPc() > 0);	
		}
		
		// If we received a complete line...
		if (pcUartReadBuffer[i] == '\r') {
			// Send the new line.
			ucSendToPc("\n\r\n", 3);
			
			
			// Command = listlog.
			if (memcmp(pcUartReadBuffer, "listlog\r", sizeof("listlog\r")) == 0) {
				vListLog();
			}	
			
			// Command = eraselog.
			else if (memcmp(pcUartReadBuffer, "eraselog\r", sizeof("eraselog\r")) == 0) {
				sprintf(pcUartWriteBuffer, "Erasing...\r\n\r\n");
				while (ucGetSendToPcSpace() < strlen(pcUartWriteBuffer)) {
					// If we are calling from task, delay for 10ms to allow other task to run.
					if (xTaskGetCurrentTaskHandle() != NULL) {
						vTaskDelay(configTICK_RATE_HZ / 100);
					}	
				}	
				ucSendToPc(pcUartWriteBuffer, strlen(pcUartWriteBuffer));
				
				vSFlashChipErase();
				
				sprintf(pcUartWriteBuffer, "Completed\r\n\r\n");
				while (ucGetSendToPcSpace() < strlen(pcUartWriteBuffer)) {
					// If we are calling from task, delay for 10ms to allow other task to run.
					if (xTaskGetCurrentTaskHandle() != NULL) {
						vTaskDelay(configTICK_RATE_HZ / 100);
					}	
				}	
				ucSendToPc(pcUartWriteBuffer, strlen(pcUartWriteBuffer));
			}
			
			// Command = getlog.
			else if (memcmp(pcUartReadBuffer, "getlog ", sizeof("getlog ") - 1) == 0) {
				// The remaining character should be the log ID.
				unsigned char ucLogId = 0;
				i = sizeof("getlog ") - 1;
				while (pcUartReadBuffer[i] != '\r') {
					ucLogId *= 10;
					ucLogId += pcUartReadBuffer[i] - 0x30;
					i++;
				}	
				
				sprintf(pcUartWriteBuffer, "Get Log %u.\r\n"
										   "Press ENTER when you are ready to continue...\r\n"
										   "Press ENTER again when finish...\r\n\r\n", ucLogId);
				while (ucGetSendToPcSpace() < strlen(pcUartWriteBuffer)) {
					// If we are calling from task, delay for 10ms to allow other task to run.
					if (xTaskGetCurrentTaskHandle() != NULL) {
						vTaskDelay(configTICK_RATE_HZ / 100);
					}	
				}	
				ucSendToPc(pcUartWriteBuffer, strlen(pcUartWriteBuffer));
				
				// Wait until we receive "enter" key.
				while (1) {
					// If there is new data available.
					if (ucGetNumberOfBytesFromPc() > 0) {
						ucReadFromPc(&pcUartReadBuffer[0], 1);
						
						if (pcUartReadBuffer[0] == '\r') {
							pcUartReadBuffer[0] = 0;
							break;
						}	
					}	
					vTaskDelay(configTICK_RATE_HZ/5);
				}	
				
				vPrintLogData(ucLogId);
				
				// Wait until we receive "enter" key.
				while (1) {
					// If there is new data available.
					if (ucGetNumberOfBytesFromPc() > 0) {
						ucReadFromPc(&pcUartReadBuffer[0], 1);
						
						if (pcUartReadBuffer[0] == '\r') {
							pcUartReadBuffer[0] = 0;
							break;
						}	
					}	
					vTaskDelay(configTICK_RATE_HZ/5);
				}	
			}	
			
			// Command = calibmag.
			else if (memcmp(pcUartReadBuffer, "calibmag\r", sizeof("calibmag\r")) == 0) {
				sprintf(pcUartWriteBuffer, "Calibrate magnetometer\r\n"
										   "Press the onboard switch when you are ready to continue...\r\n"
										   "Press the switch again when finish...\r\n\r\n");
				while (ucGetSendToPcSpace() < strlen(pcUartWriteBuffer)) {
					// If we are calling from task, delay for 10ms to allow other task to run.
					if (xTaskGetCurrentTaskHandle() != NULL) {
						vTaskDelay(configTICK_RATE_HZ / 100);
					}	
				}	
				ucSendToPc(pcUartWriteBuffer, strlen(pcUartWriteBuffer));
				
				vCalibrateMag();
			}	
			
			// Command = getconfig.
			else if (memcmp(pcUartReadBuffer, "getconfig\r", sizeof("getconfig\r")) == 0) {
				vPrintFlashData();
			}	
			
			// Invalid command.
			else {
				sprintf(pcUartWriteBuffer, "Invalid command\r\n\r\n");
				while (ucGetSendToPcSpace() < strlen(pcUartWriteBuffer)) {
					// If we are calling from task, delay for 10ms to allow other task to run.
					if (xTaskGetCurrentTaskHandle() != NULL) {
						vTaskDelay(configTICK_RATE_HZ / 100);
					}	
				}	
				ucSendToPc(pcUartWriteBuffer, strlen(pcUartWriteBuffer));
			}	
			
			// Reset the pointer and data buffer.
			for (i = 0; i < sizeof(pcUartReadBuffer); i++) {
				pcUartReadBuffer[i] = 0;
			}
			i = 0;	
			
			// Ready for next command.
			ucSendToPc(">", 1);
		}	
		
			
		
		// Record the stack high water mark.
		xStackWaterMark.uiCml = uxTaskGetStackHighWaterMark(NULL);
		
		// Set the error flag if the water mark is too low.
		if (xStackWaterMark.uiCml < RTOS_STACK_THRESHOLD) {
			xSystemError.bRtosStackError = 1;
		}
		
		// Delay for 20ms (50Hz).
		vTaskDelayUntil(&xLastFlashTime, configTICK_RATE_HZ / 50);
	}	
}	
